

/* 
		Filename:  our_fstream.cpp
		Created Date:  2/18/11
		Author:      Jason Frank

		Purpose:  This class extends C++'s fstream class to better fit what we need for our file system
		          I/O.  It will abstract away a lot of painful details that we must do just to do
							basic reads and write to/from our "disk".  

							Such tasks include bit-shifting and byte-swapping, to enable 4-bytes ints to be
							properly written to disk, and to deal with Big-endian/Little-endian swapping
							issues that we have found to be a problem.

							Another common thing that needs to be done with reads and writes is to check for 
							errors after every little mini-step (check after seeking, check after writing,
							check after flushing...).  The functions in this class check for all such errors.
							Therefore, most functions take the calling function's name as a parameter, so that 
							the functions of this object can report to the user where the error occurred. 

*/ //

//#include "SimpleFS.h"
#include "our_fstream.h"



///////////////////////////////
/////// OVERRIDDEN MTHODS /////

// seekg()
istream& our_fstream::seekg( streamoff offset, ios_base::seekdir dir, string calling_func_name )
{
	//cout << "\nYou just called our_fstream::seekg(), from " << calling_func_name << "\n" << endl;
	fstream::seekg(offset, dir );  // call superclass func
	if(fstream::fail())   { 
		cout << "Error in " << calling_func_name <<": seekg("<<offset << ","<<dir << ") must have failed\n"; 
		if( WINDOWS_DEV ) { system("pause"); } 
		exit(-1); 
	}

	return *this;
} //// end seekg() ////

// seekp()
ostream& our_fstream::seekp( streamoff offset, ios_base::seekdir dir, string calling_func_name )
{
	fstream::seekp(offset, dir );  // call superclass func
	if(fstream::fail())   { 
		cout << "Error in "<<calling_func_name <<": seekp("<<offset << ","<<dir << ") must have failed\n"; 
		if( WINDOWS_DEV ) { system("pause"); } 
		exit(-1); 
	}

	return *this;
}  //// end seekp() ////

// write()
ostream& our_fstream::write(const char* s , streamsize n, string func_name )
{
	fstream::write(s,n); // call superclass func

	if(fstream::bad())  { 
		cout << "Error in "<<func_name<<": write() must have failed\n"; 
		if( WINDOWS_DEV ) { system("pause"); } 
		exit(-1); 
	}
	// flush output buffer right away
	fstream::flush();
	if(fstream::bad())  { 
		cout << "Error in "<<func_name<<": flush() after a write() must have failed\n"; 
		if( WINDOWS_DEV ) { system("pause"); } 
		exit(-1); 
	}
	return *this;
} //// end write() /////

// read()
istream& our_fstream::read( char* s, streamsize n, string func_name)
{
	fstream::read(s,n); // call superclass func

	if(fstream::bad())  { 
		cout << "Error in "<<func_name<<": read() must have failed\n"; 
		if( WINDOWS_DEV ) { system("pause"); } 
		exit(-1); 
	}
	return *this;
} //// end read() ////


/////////////////////////////////////
///// ADDED METHODS /////////////////

// Function: int_to_4byte_char()
// Purpose:  Puts the "val" into 4 bytes of the char array (does bit shifting to keep all precision)
// IMPORTANT: this method assumes that cstr is a 4 element char array (already allocated)!!!!!
void our_fstream::int_to_4byte_char(unsigned int val, char* cstr, string func_name )
{
	// Method from:  http://stackoverflow.com/questions/3784263/converting-an-int-into-a-4-byte-char-array-c 

	if(sizeof(val) != 4) { 
		cout << "\nError bit-shifting in "<<func_name << ": sizeof(val) != 4.  Code assumes int size of 4 bytes!\n"; 
		if( WINDOWS_DEV ) { system("pause"); } 
		exit(-1); 
	}
	cstr[0] = (val >> 24) & 0xFF;
	cstr[1] = (val >> 16) & 0xFF;
	cstr[2] = (val >> 8)  & 0xFF;
	cstr[3] =  val        & 0xFF;
} //// end int_to_4byte_char() /////


// Function: reverse_4bytes()
// Purpose:  Changes the order of the 4 bytes of "val" to the reverse.  This is needed if bytes get written
//           to disk in opposite order as they were in memory (big-endian/little-endian issue).
unsigned int our_fstream::reverse_4bytes(unsigned int val, string func_name )
{
	if(sizeof(val) != 4) { 
		cout << "\nError byte-swapping in "<<func_name << ": sizeof(val) != 4.  Code assumes int size of 4 bytes!\n"; 
		if( WINDOWS_DEV ) { system("pause"); } 
		exit(-1); 
	}
	//  from: http://stackoverflow.com/questions/544928/reading-integer-size-bytes-from-a-char-array
	
	unsigned int reversed = (unsigned int)(((val & 0xFF000000) >> 24) |
                                         ((val & 0x00FF0000) >>  8) |
                                         ((val & 0x0000FF00) <<  8) |
                                         ((val & 0x000000FF) << 24));
	return reversed;
} //// end reverse_4bytes() //////



// Function: fread()
// Purpose:  seek to position (to "offset" from beg of file), then read "num_bytes" into "buf"
// IMPORTANT: caller must make sure "buf" is large enough!
istream& our_fstream::fread( int offset, char* buf, streamsize num_bytes, string func_name)
{
	this->seekg(offset, ios_base::beg, func_name );

	this->read(buf,num_bytes,func_name); // read into buffer "buf"

	return *this;
} //// end fread() /////


// type_read(): Highest-level of all the read funcs!  Use this one usually.
//              Completely handles getting 4 bytes off of disk (at "offset") 
//              and putting them into what "int_var_ptr" points to. 
// IMPORTANT: "int_var_ptr" must already have memory allocated to it
istream& our_fstream::type_read( int offset, unsigned int* int_var_ptr, string func_name)
{
	char buf[sizeof(int)];
	unsigned int casted, reversed;
	
	// read into buf (seeks too)
	this->fread(offset, buf, sizeof(int), func_name );
	// cast it 
	casted = *(reinterpret_cast<unsigned int*>(buf));

	// finally, reverse the 4 bytes to deal with little-endian/big-endian changover
	reversed = this->reverse_4bytes(casted,func_name);

	*int_var_ptr = reversed;  // provide user with desired int

	return *this;
} ///// end type_read() ///////


istream& our_fstream::type_read_at_cur_pos( unsigned int* int_var_ptr, string func_name)
{
	char buf[sizeof(int)];
	unsigned int casted, reversed;

	// read into buf (don't seek)
	this->read(buf,sizeof(int), func_name );

	// cast it 
	casted = *(reinterpret_cast<unsigned int*>(buf));

	// finally, reverse the 4 bytes to deal with little-endian/big-endian changover
	reversed = this->reverse_4bytes(casted,func_name);

	*int_var_ptr = reversed;  // provide user with desired int

	return *this;
} ////// end type_read_at_cur_pos() /////////////////


// Function: fwrite()
// Purpose:  seek to position (to offset from beg of file), then write num_bytes from buf to disk (flush too)
ostream& our_fstream::fwrite( int offset, const char* buf, streamsize num_bytes, string func_name)
{
	this->seekp(offset, ios_base::beg, func_name );

	this->write(buf,num_bytes,func_name); // write() also flushes

	return *this;
} //// end fwrite() /////



// type_write() #1 (overloaded func)
ostream& our_fstream::type_write( int offset, unsigned int var, string calling_func_name)
{
	char buf[sizeof(var)];

	// get the unsigned int into 4-byte char array
	this->int_to_4byte_char(var, buf, calling_func_name);

	// now ready to write to disk.  seek, write, and flush:
	this->fwrite(offset, buf, sizeof(var), calling_func_name);

	return *this;
} //// end type_write() #1 ////


/*
// type_write() #2 (overloaded func)
ostream& our_fstream::type_write( int offset, const FList_node& fl_node, string calling_func_name)
{
	char buf[sizeof(fl_node)];

	// get the fields into 4-byte char array
	this->int_to_4byte_char(fl_node.size, buf, func_name);

	this->fwrite(offset,buf,sizeof(fl_node.size),func_name);


	return *this;
} //// end type_write() #2 ////
*/


// type_write() #3 (overloaded func)
// Iteratre through all ints of the vec
ostream& our_fstream::type_write( int offset, const vector<unsigned int>& vec, string func_name)
{
	unsigned int size = vec.size();
	
	for( unsigned int i = 0; i < size; i++ )  {
		this->type_write((offset + i*sizeof(unsigned int)), vec[i], func_name);
	}

	return *this;
} //// end type_write() #3 ////




// Function: type_write_at_cur_pos().  (Doesn't do any seeking)
ostream& our_fstream::type_write_at_cur_pos(  unsigned int var, string calling_func_name)
{
	char buf[sizeof(var)];

	// get the unsigned int into 4-byte char array
	this->int_to_4byte_char(var, buf, calling_func_name);
	// don't seek, just write and flush
	this->write(buf, sizeof(var), calling_func_name);

	return *this;
}//// end type_write_at_cur_pos() /////